<?php
/**
 * 数据库存储 SESSION 类
 *
 * @version 1.0
 *
 * @author CoseyLee@gmail.com
 *
 */

class session_db
{
	var $lifetime = 1800; // 默认与PHP配置值相同，会在关闭SESSION时处理过期数据。
	var $db = NULL;
	public function __construct()
	{
		$this -> db = random::model('session');
		session_set_save_handler(
			array($this, 'open'),
			array($this, 'close'),
			array($this, 'read'),
			array($this, 'write'),
			array($this, 'destroy'),
			array($this, 'gc')
		);
		session_start();
	}

	public function open($save_path, $session_name)
	{
		return true;
	}

	public function close()
	{
		$this -> gc($this -> lifetime);
		return true;
	}

	public function read($session_id)
	{
		$sess = $this -> db -> select_one('data, client_ip', "`session_id` = '{$session_id}'");
		if ( !empty($sess) && $sess['client_ip'] == GetIP() )
		{
			return $sess['data'];
		}
		else
		{
			return  '';
		}
	}

	public function write($session_id, $data)
	{
		/**
		 * 同一用户只可在一点登录
		 * 在某一时刻两条同ADMINID的SESSION会同时存在，最先触发下次写入时的SESSION将会保留，而不是最后登录者。
		 * 此时刻就是登录跳转之前，$_SESSION['safekey'] = $safekey;之后，两条同时存在。
		 * 因为下次触发就会删除所有SESSION，并将触发者的SESSION重新写入（唯一）。
		 * 最先触发下次SESSION者将被保留。
		 */
		
		$adminid = isset($_SESSION['adminid']) ? $_SESSION['adminid'] : 0;
		$sess = $this -> db -> select('session_id', "`adminid` = '{$adminid}'");
		$rows = $this -> db -> num_rows($sess);
		if ($rows > 1 && $adminid != 0)
		{
			/**
			 * 可以采用以下语句，但为了给用户以提示区别不同退出。
			 * $this -> db -> delete("`adminid` = '{$adminid}'");
			 * 故设置$_SESSION['isOut'] = true;，区别被踢出与超时退出。
			 */
			$this -> db -> update(array('data' => 'isOut|b:1;'), "`adminid` = '{$adminid}'");
		}
		$sess = $this -> db -> select_one('session_id', "`session_id` = '{$session_id}'");
		if (isset($sess['session_id']) && !empty($sess))
		{
			$this -> db -> update(array('data' => $data, 'update_time' => time(), 'adminid' => $adminid), "`session_id` = '{$session_id}'");
		}
		else
		{
			$this -> db -> insert(array('session_id' => $session_id, 'client_ip' => GetIP(), 'update_time' => time(), 'adminid' => $adminid, 'data' => $data));
		}
		return true;
	}

	public function destroy($session_id)
	{
		$this -> db -> delete("`session_id` = '{$session_id}'");
		if (isset($_COOKIE[session_name()]))
		{
			setcookie(session_name(), '', time() - 999999999);
		}
		return true;
	}

	public function gc($maxlifetime)
	{
		$exp_time = time() - $maxlifetime;
		$this -> db -> delete("`update_time` < '{$exp_time}'");
		return true;
	}
}
